# -*- coding: utf-8 -*-
from pycube.core.odict import OrderedDict as dict
from logic import Logic

from re import search,sub,match,findall as find,compile
from os.path import exists as file_exists
import os
from sys import exit

import time
import md5,re,hashlib
#fdsf
class template:
    def __init__(self,req,project_path,debug,config,admin=False):

        """
            Init of the templates class.
            Defining the template path from config, checking
            for admin or site templates, parsing default block [main]        
        
        """
        self.start=time.time()

        self.conf=config
        self.debug=debug
        self.req=req
        self.path=project_path+self.conf['site_tpl']
        self.cacheF=project_path+self.conf['site_cache']
        self.main=self.conf['site_main']+self.conf['file_ext']
        self.content=""
        self.collector=""
        self.assign=dict()
        self.blocks_loaded={}
        self.loaded=[]
        self.cached_templates=[]

        #Functions
        self.cache_logic=Logic()
        self.logic={}
        self.weHaveBuiltFunctions=False

        self.logic_builtin_functions=__import__('pycube.core.template.functions',globals(),locals(),[''],-1)
        self.logic_extended_functions=False

        if 'functions' in self.conf:
            try:
                self.logic_extended_functions=__import__(self.conf['functions'],globals(),locals(),[''],-1)
            except ImportError,e:
                raise Exception('System was unable to find custom template functions module "%s"'%self.conf['functions'])






        self.insert={}

        if not os.path.exists(self.path):
            os.makedirs(self.path)
        if not os.path.exists(self.cacheF):
            os.makedirs(self.cacheF)
        if self.debug:
            if not file_exists(self.path+self.main): open(self.path+self.main,'w').write('Your main file is empty')
            if not file_exists(self.cacheF+'!sum.md5'): open(self.cacheF+'!sum.md5','w')

            if not file_exists(self.cacheF+'functions.data'):
                open (self.cacheF+self.conf['md5_file'],'w').write('')


        try:

            self.logic=eval(open(self.cacheF+'functions.data','r').read())

        except:
            self.logic={}



        self.__add(self.conf['site_main'])
        self.content+=self.block(self.conf['site_main']+self.conf['file_ext'])



    def __cache_try(self,filename):


        """        
        
            Caches the templates. This is system function
            It's impossible to use this function outside this class
            First - collecting all the 
            tag names and the pushes them into cache          
        
        """

        if filename in self.cached_templates:
            return

        if self.debug:
            if not file_exists(self.path+filename):
                open(self.path+filename,'w').write('this is file is empty')





        file=open(self.path+filename,'rb')
        md5=hashlib.md5()
        buffer=file.read(2**20)
        while buffer:
            md5.update(buffer)
            buffer=file.read(2**20)
        sum=md5.hexdigest()

        def getmd5(f,l):
            s=search(f+'='+'(.*?);\r\n',l,re.DOTALL)
            if s:
                return s.group(1)



        lines=open (self.cacheF+self.conf['md5_file'],'r').read()

        if  sum!=getmd5(filename,lines):
            #self.write('Refreshing Cache')

            #self.__wakeup_proccess_killing_monitor__()
            macrosList=[]
            file_content=trash=open (self.path+filename,'r').read()

            for macros in find('<!-- start:(\w{1,25}) -->',file_content,re.DOTALL): macrosList.append(macros)

            for item in macrosList:
                trash=sub(compile ("<!-- start:"+item+" -->.*?<!-- end:"+item+" -->",re.DOTALL),self.conf['item_start']+item+self.conf['item_end'],trash)


            cache=[ "[%s]%s[/%s]"%(filename,self.clearSpaces(trash),filename) ]

            for item in macrosList:

                for data in find("<!-- start:"+item+" -->(.*?)<!-- end:"+item+" -->",file_content,re.DOTALL):

                    for macros in find('<!-- start:(\w{1,25}) -->',data,re.DOTALL):
                        data=sub(compile ("<!-- start:"+macros+" -->.*?<!-- end:"+macros+" -->",re.DOTALL),self.conf['item_start']+macros+self.conf['item_end'],data)

                    cache.append("[%s]%s[/%s]"%(item,self.clearSpaces(data),item))


            data_with_prepared_functions=self.cache_logic.build(filename,''.join(cache))

            #Using temporary logic dictionary    
            funcDict={}
            if not file_exists(self.cacheF+'functions.data'):
                open (self.cacheF+'functions.data','w').write(str(self.cache_logic.hash))
            else:
                funcDict=eval(open(self.cacheF+'functions.data','r').read())
                #Updating dict                
                for key,func_values in self.cache_logic.hash.items():
                    funcDict[ key ]=func_values
                #Writing fulldatas
                open (self.cacheF+'functions.data','w').write(str(funcDict))



            self.logic=funcDict
            self.weHaveBuiltFunctions=True

            open(self.cacheF+filename+self.conf['cache_ext'],'w').write(data_with_prepared_functions)

            self.cached_templates.append(filename)


            #Writing new MD5 SUM
            if match('.*?'+filename+'='+'(.*?);',lines,re.DOTALL):
                open (self.cacheF+self.conf['md5_file'],'w').write(sub(filename+'='+'(\w+?);',filename+'='+sum+";",lines))
            else:
                open (self.cacheF+self.conf['md5_file'],'w').write(lines+filename+'='+sum+";\r\n")



    def clearSpaces(self,content):

        string=re.sub(re.compile("\r\n",re.DOTALL),'',content)
        string=re.sub(re.compile("/\*.*?\*/",re.DOTALL),'',string)
        string=re.sub(re.compile("\t{1,100}",re.DOTALL),'',string)
        string=re.sub(re.compile("\n{1,100}",re.DOTALL),'',string)
        #string = re.sub(re.compile(r'\[\/(.*?)\]', re.DOTALL),"[/\\1]",string)

        return string

    #Adding to trash
    def __add(self,filename):

        """         
            Adding pointed template file to main collector
            The first agrument is the name of the file without
            it extention.
        
            self.add( 'someblock' )
        
            This function is private only.       
        
        """

        if filename not in self.loaded:

            fName=self.cacheF+filename+self.conf['file_ext']+self.conf['cache_ext']
            if self.debug:
                self.__cache_try(filename+self.conf['file_ext'])

            self.collector+=open (fName ,'r').read()
            self.loaded.append(filename)


    def __uniq(self,macros,data):

        """                
            Uniq parse of the templates
            This simple function just replacing the agrument content width
            dictionary
        """


        for item,value in macros.items():
            data=data.replace(self.conf['item_start']+item+self.conf['item_end'],str(value))
            del(macros[ item ])
        return data

    def __load_function_module__(self,function_name):

        """
        Load template functions. Searching them in core functions.
        Then, we try to reach user defined module        
        """


        iFunction=False

        if function_name in dir(self.logic_builtin_functions):
            iFunction=self.logic_builtin_functions.__getattribute__(function_name)

        if self.logic_extended_functions:
            loadExtended=True
            if function_name in dir(self.logic_builtin_functions) and function_name in dir(self.logic_extended_functions):
                if self.debug:
                    raise Exception('Template function "%s" was already defined in built in template functions'%function_name)

                loadExtended=False

            if function_name in dir(self.logic_extended_functions):
                if loadExtended:
                    iFunction=self.logic_extended_functions.__getattribute__(function_name)
        if self.debug:
            if not iFunction:
                raise Exception('Template function "%s" is not defined'%function_name)


        return iFunction

    def __do_magic_with_logic__(self,input,keys=None):

        """
        Accepting input data with keys.
        Returning text with the result of template functions
        """


        data=find('<<([a-z0-9_]+?)>>',input,re.DOTALL)
        for func_name in data:
            if func_name in self.logic:

                iObject=self.logic[ func_name ]

                iExecutable=self.__load_function_module__(iObject['function'])

                if iExecutable:
                    iObject=self.logic[ func_name ]

                    vars_dict={}
                    if keys:
                        for variable in iObject['variables']:
                            if variable in keys:
                                vars_dict[ variable ]=keys[variable]

                    resultData=iExecutable(self.req,self,iObject['static'],vars_dict)

                    if resultData:
                        input=input.replace('<<%s>>'%func_name,str(resultData))
                    else:
                        input=input.replace('<<%s>>'%func_name,'')
                else:
                    input=input.replace('<<%s>>'%func_name,'')
            else:
                input=input.replace('<<%s>>'%func_name,'')

        return input


    def block(self,block,assign=False):
        """
        Returns the content of template file
        """

        if block not in self.blocks_loaded:

            fileFind=match("^(\w+?)"+"\\"+self.conf['file_ext'],block)
            if fileFind:
                self.__add(fileFind.group(1));  block=fileFind.group(0)

            elif match("^\w+?\.\w+?",block):
                case=block.split(".");  block=case[1];  self.__add(case[0])

            result=find('\['+block+'\](.*?)\[/'+block+'\]',self.collector,re.DOTALL)
            if result:
                self.blocks_loaded[ block ]=result[0]
                f=self.__do_magic_with_logic__(self.blocks_loaded[ block ] ,assign)

                return self.__uniq(assign,f) if assign else f
            else:
                return ''

        else:

            f=self.__do_magic_with_logic__(self.blocks_loaded[ block ] ,assign)

            return self.__uniq(assign,f) if assign else f


    #clear macros
    def clear(self,item):
        if type(item).__name__=='str':
            if item in self.insert:  self.insert[ item ]=''

    #Destroy earlier assigned block
    def destroy(self,item):
        if type(item).__name__=='str':
            if item in self.assign:
                del (self.assign[ item ])

    #Empty already parsed templates
    def empty(self): self.content=""

    #Pushing block to pointed macros
    def push(self,block,position,assign=None):
        """
        Pushes block into macros
        """
        html=self.block(block,assign)
        if html:
            if position in self.assign:
                self.assign[ position ].append(html)
            else:
                self.assign[ position ]=[ html ]



    #Searching for some html tag (already parsed) and replacing it's value
    def tag(self,tagname,value):
        mask="<%s>(.*?)<\/%s>"%(tagname,tagname)
        for key in find(mask ,self.content,re.DOTALL):
            self.content=self.content.replace("<"+tagname+">"+key+"</"+tagname+">","<"+tagname+">"+value+"</"+tagname+">")

    #ajax Request
    def ajax(self,macros,assign=None):
        """
        Replacing content with on block
        """
        del (self.assign)
        self.assign=dict()
        self.content=self.block(macros,assign) if assign else  self.block(macros)

    #Parsing templates 
    def parse(self,block,assign=False,ajax=False):
        """
        Parsing block. Like "What you see is what you get"
        """
        html=self.block(block,assign)
        if html:
            if block in self.assign:
                self.assign[block].append(html)
            else:
                self.assign[block]=[ html ]


    def __del(self):
        del(self.blocks_loaded)


    def done(self):
        for macros,data in self.assign.items():
            self.content=self.content.replace(self.conf['item_start']+macros+self.conf['item_end'],''.join(data))

        self.content=sub(compile(r'{%\w+?%}',re.DOTALL),"",self.content)

        self.content=re.sub(re.compile('<js:(.*?)\/>',re.DOTALL),r'<script src="/usr/js/\1" type="text/javascript"></script>',self.content)

        end=str(time.time()-self.start)
        return self.content










